﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;


namespace ShuDou
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            _ActiveGongIndex = -1;
            _ActiveList = null;
            _ActiveClickList = null;
            Cellsd.ShowCanList = false;
            InitData();
            InitDgv();
            DataToDgv();
            //TestData();

        }       
        private void InitData()
        {
            _Data = new Cellsd[9, 9];
            for (int row = 0; row < 9; row++)
                for (int col = 0; col < 9; col++)
                    _Data[row, col] = new Cellsd();
            CellR._Data = _Data;
            CellC._Data = _Data;
            CellG._Data = _Data;
            _ListR = new List<CellR>();
            for (int row = 0; row < 9; row++)
                _ListR.Add(new CellR(row));
            _ListC = new List<CellC>();
            for (int col = 0; col < 9; col++)
                _ListC.Add(new CellC(col));
            
            // gong init
            _ListG = new List<CellG>();
            for (int row = 0; row < 9; row++)
            {
                for (int col = 0; col < 9; col++)
                {
                    int gongindex = row / 3 * 3 + col / 3;
                    if (gongindex == _ListG.Count)
                    {
                        _ListG.Add( new CellG(gongindex));
                    }
                    _ListG[gongindex].Addlist(_Data[row, col]);
                }
            }
            for (int row = 0; row < 9; row++)
            {
                for (int col = 0; col < 9; col++)
                {
                    _Data[row, col].R = _ListR[row];
                    _Data[row, col].C = _ListC[col];

                    int gongindex = row / 3 * 3 + col / 3;
                    _Data[row,col].G = _ListG[ gongindex  ];
                }
            }

            _ListGCR = new List<CellList>();
            foreach (CellR G in _ListR)
                _ListGCR.Add(G);
            foreach (CellC G in _ListC)
                _ListGCR.Add(G);
            foreach (CellG G in _ListG)
                _ListGCR.Add(G);

        }
        private void ClearData()
        {
            Cellsd.ShowCanList = false;
            foreach (Cellsd C in _Data)
            {
                C.Clear();
            }
        }
        private void InitDgv()
        {
            dgv.ColumnCount = dgv.RowCount = 9;
            dgv.RowTemplate.Height = dgv.Height / 9 - 4;
            foreach (DataGridViewColumn dc in dgv.Columns)
                dc.Width = dgv.Width / 9  -3;
        }
        private void DataToDgv()
        {
            for (int row = 0; row < 9; row++)
                for (int col = 0; col < 9; col++)
                    dgv[col, row].Value = _Data[row, col];
        }
        private void dgv_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
        {
            Cellsd C = _Data[e.RowIndex, e.ColumnIndex];
            if (C.IsOrigin)
            {
                e.CellStyle.ForeColor = Color.Red;
            }
            if ((e.RowIndex == 3 || e.RowIndex == 4 || e.RowIndex == 5)
               && (e.ColumnIndex == 3 || e.ColumnIndex == 4 || e.ColumnIndex == 5))
                e.CellStyle.BackColor = Color.Pink;
            else
            if (e.RowIndex == 3 || e.RowIndex == 4 || e.RowIndex == 5
                || e.ColumnIndex == 3 || e.ColumnIndex == 4 || e.ColumnIndex == 5)
                e.CellStyle.BackColor = Color.LightGray;

            if (C.V == -1 && C.ToString() != "")
            {
                Font ft = dgv.DefaultCellStyle.Font;
                Font ft2 = new System.Drawing.Font(ft.FontFamily,10);
                e.CellStyle.Font = ft2;
            }

            //if (C.C.ColIndex == _ActiveGongIndex)
            //    e.CellStyle.BackColor = Color.Red;
            if(_ActiveClickList !=null)
                if(_ActiveClickList.Contains(C))
                    e.CellStyle.BackColor = Color.Yellow;

            if(_ActiveList!=null)
                if(_ActiveList.Contains(C))
                    e.CellStyle.BackColor = Color.DeepPink;

        }
        private void dgv_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
        {
            if (_UseMode)
            {
            }
            else
            {
                return;
            }
        }
        private void dgv_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex == -1 || e.ColumnIndex == -1 || _Data == null)
            {
                _ActiveClickList = null;
                return;
            }
            Cellsd C = _Data[e.RowIndex, e.ColumnIndex];
            if (C != null && C.V < 0)
                _ActiveClickList = C.CRG;
            else
                _ActiveClickList = null;
            dgv.Invalidate();
        }
        private void dgv_KeyUp(object sender, KeyEventArgs e)
        {
            if(dgv.SelectedCells.Count == 0 ) return;
            int row = dgv.SelectedCells[0].RowIndex ; 
            int col = dgv.SelectedCells[0].ColumnIndex;
             if (_UseMode && row!= -1 && col != -1)
            {
                if (e.KeyData > Keys.D0 && e.KeyData <= Keys.D9)
                {
                    int V = e.KeyData - Keys.D0;
                    _Data[row, col].V = V;
                    _Data[row, col].IsOrigin = true;
                    dgv.ClearSelection();
                    //dgv.InvalidateCell(col, row);
                }
                else if (e.KeyData == Keys.Delete)
                {
                    _Data[row, col].Clear();
                    dgv[col,row].Value = "";
                    dgv[ col,row].Value = _Data[row, col];
                    dgv.ClearSelection();
                    //dgv.InvalidateCell(col, row);
                }

            }
        }

        private void buttonInit_Click(object sender, EventArgs e)
        {
            _UseMode = true;
            ClearData();
            dgv.Invalidate();
            MessageBox.Show("已经初始化，请设置值");
        }
        private void buttonClose_Click(object sender, EventArgs e)
        {
            _UseMode = false;         
            foreach (Cellsd C in _Data)
                if (C.V > 0)
                    C.IsOrigin = true;
            dgv.Invalidate();
        }
        private void button1ComputFirst_Click(object sender, EventArgs e)
        {
            InitEveryCanList();
            Compute();
            Cellsd.ShowCanList = true;
            dgv.Invalidate();
        }
        private void buttonReCompute_Click(object sender, EventArgs e)
        {
            Cellsd.ShowCanList = true;
            Compute();
            dgv.Invalidate();
        }
        private void buttonComputeByCompairCanList_Click(object sender, EventArgs e)
        {
            if(!ComputeByCompairCanlist(true))
                ComputeByCompairCanlist(true,2);           
        }
        private void buttonComputeActiveList_Click(object sender, EventArgs e)
        {
            if (checkBox1.Checked)
            {
                if (dgv.SelectedCells.Count != 1) return;
                Cellsd C = _Data[dgv.SelectedCells[0].RowIndex, dgv.SelectedCells[0].ColumnIndex];
                foreach (CellList CL in C.LCRG)
                {
                    _ActiveList = CL.list;
                    if (ComputeByCompairCanList(_ActiveList, 2))
                        break;
                }
                dgv.Invalidate();
            }
            if(_ActiveList!=null)
                if (!ComputeByCompairCanList(_ActiveList,1, false))
                {
                    MessageBox.Show("ComputeActiveList.Error");
                }
                else
                {
                    dgv.Invalidate();
                }
        }
        private void buttonExport_Click(object sender, EventArgs e)
        {
            StringBuilder sb = ExportShuDu();
            textBox1.Text = sb.ToString();
        }
        private void buttonImport_Click(object sender, EventArgs e)
        {
            ClearData();
            ImportData(textBox1.Text);
            buttonClose.PerformClick();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            _TryCnt = 0;
            System.DateTime dt = DateTime.Now;
            String Msg = "";
            if (checkBoxBaoLi.Checked)
            {
                if (PoJia())
                    Msg ="经过" + _TryCnt + "次测试，最大回撤深度:"+_MaxBackDeepth ;
                else 
                {
                    MessageBox.Show("对不起，连暴力破解都失败了，");
                    return;
                }
            }
            else
            {
                if (DecoderOnce())
                    Msg = "经过" + _TryCnt + "次努力,";
                else
                {
                    MessageBox.Show("对不起，我已经尽力了");
                    return;
                }
            }
            dgv.Invalidate();
            TimeSpan ts = System.DateTime.Now - dt;
            Msg += " 共耗时" + ts.Seconds + "秒又" + ts.Milliseconds + "毫秒，,终于成功破解！！";
            MessageBox.Show(Msg);



            //_ActiveGongIndex++;
            //_ActiveGongIndex %= 9;
            //dgv.Invalidate();

            //_UseMode = !_UseMode;
            //if (!_UseMode)
            //    button1.Text = "临时改值--完成";
            //else
            //    button1.Text = "临时改值--允许修改";

        }
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            _UseMode = checkBox1.Checked;
        }
        private void InitEveryCanList()
        {
            foreach (CellList cl in _ListGCR)
                cl.InitExistCanList();
            foreach (Cellsd C in _Data)
                C.InitCanList();
        }
        private bool Compute()
        {
            bool re = false;
            //InitEveryCanList();
            foreach (Cellsd C in _Data)
                if (C.V < 0 && C.CanList.Count == 1)
                {
                    C.SetValueByCanList();
                    re = true;
                }            
            return re;
        }
        private Boolean ComputeByCompairCanlist( bool TestPos = false,int compaircnt = 1)
        {
            bool re = false;
            foreach (CellList G in _ListGCR)
            {
                if (ComputeByCompairCanList(G.list,compaircnt, TestPos))
                {
                    _ActiveList = G.list;
                    dgv.Invalidate();
                    return true;
                }
            }
            _ActiveList = null;
            return re;
        }
        private bool DecoderOnce( )
        {
            int countcnt = 200;
            int trycnt=0;
            InitEveryCanList();
            Cellsd.ShowCanList = true;
            dgv.Invalidate();
            while (trycnt < countcnt)
            {
                if (!Compute())
                {
                    if (!ComputeByCompairCanlist())
                    {
                        if (!ComputeByCompairCanlist(false, 2))
                            //if (!ComputeByCompairCanlist(false, 3))
                                ComputeByCompairCanlist(false, 3);
                    }
                }
                trycnt++;

                bool bcontinue = false;
                foreach (Cellsd C in _Data)
                    if (C.V < 0)
                    {
                        bcontinue = true;
                        break;
                    }
                if (bcontinue)
                    continue;
                _TryCnt = trycnt;
                _ActiveClickList = null;
                _ActiveList = null;
                return true;
                //break;
            }
            return false;
        }
        private bool PoJia()
        {            
            //string save = ExportShuDu().ToString();
            bool bPJ = true;
            InitEveryCanList();
            List<Cellsd> PJcells = new List<Cellsd>();
            foreach(Cellsd  cc in _Data)
                if(cc.V<0)
                    PJcells.Add(cc);
            PJcells.Sort( (r1,r2) => r1.CanList.Count - r2.CanList.Count);

            foreach(CellList cc in _ListGCR)
            {
                cc.ClearPJ();
            }

            int index = 0;
            int setcnt = 0;
            int maxbackdeep = 0;
            int maxi = 0;
            Stack<CellIndex> stack = new Stack<CellIndex>() ;
            for(int i=0; i<PJcells.Count; setcnt++ )
            {
                if(SetCell( PJcells[i],index,stack))
                {
                    i++;
                    index = 0;
                    maxi = maxi > i ? maxi : i;
                }else
                {
                    if(stack.Count == 0)
                    {
                        bPJ = false;
                        break;
                    }
                    CellIndex ci = stack.Pop();
                    int V = ci.Cell.CanList[ci.Index];
                     foreach(CellList cl in ci.Cell.LCRG)
                        cl.RemovePJ(V);
                    i--;
                    index = ci.Index + 1;
                    maxbackdeep = maxbackdeep > maxi - i ? maxbackdeep : maxi - i;
                }
            }
            if(stack.Count == PJcells.Count)
                bPJ = true;
            _TryCnt = setcnt;
            _MaxBackDeepth = maxbackdeep;
            return bPJ;
        }
        public bool SetCell(Cellsd C, int index, Stack<CellIndex> stack)
        {
            while(index < C.CanList.Count)
            {
                int V = C.CanList[index];
                if(C.C.PJ.Contains(V) || C.R.PJ.Contains(V) || C.G.PJ.Contains(V)){
                    index++;
                }else{
                    stack.Push( new CellIndex(C,index));
                    C.V = V;
                    foreach(CellList cl in C.LCRG)
                        cl.AddPJ(V);
                    return true;
                }
            }
            if(index == C.CanList.Count)
                return false;
            return false;
        }

        private void ImportData(string data)
        {
            string[] ss = data.Split(',');
            int index = 0;
            foreach (Cellsd C in _Data)
            {
                if (ss[index].Trim() != "")
                    C.V = Convert.ToInt32(ss[index].Trim());
                index++;
            }
        }
        private StringBuilder ExportShuDu()
        {
            StringBuilder sb = new StringBuilder();
            for (int row = 0; row < 9; row++)
            {
                for (int col = 0; col < 9; col++)
                {
                    if (_Data[row, col].V > 0)
                    {
                        if (checkBoxorigin.Checked && !_Data[row, col].IsOrigin)
                            sb.Append(" ,");
                        else
                            sb.Append(_Data[row, col].V + ",");
                    }
                    else
                        sb.Append(" ,");
                }
                sb.AppendLine();
            }
            return sb;
        }
        private static bool ComputeByCompairCanList(List<Cellsd> list, int compaircnt = 1, bool testpos = false)
        {
            bool re = false;
            if (compaircnt > 1)
            {
                List<Cellsd> cpr = list.Where(r => r.V < 0 && r.CanList.Count == compaircnt).ToList();
                List<string> cprstr = cpr.Select(r => r.ToString()).ToList();
                List<string> distinctstr = cprstr.Distinct().ToList();
                if (cprstr.Count - distinctstr.Count == compaircnt - 1)
                {
                    Dictionary<string, int> dic = new Dictionary<string, int>();
                    foreach (Cellsd cc in cpr)
                    {
                        string str = cc.ToString();
                        if (!dic.ContainsKey(str))
                            dic[str] = 1;
                        else
                            dic[str]++;
                    }
                    foreach (Cellsd cc in cpr)
                    {
                        string str = cc.ToString();
                        if (dic[str] == compaircnt)
                        {
                            foreach (Cellsd removecc in list)
                            {
                                if (removecc.V < 0 && removecc.ToString() != str)
                                {
                                    foreach (int removei in cc.CanList)
                                    {
                                        if (removecc.CanList.Contains(removei))
                                        {
                                            removecc.CanList.Remove(removei);
                                            re = true;
                                        }
                                    }
                                }
                            }
                            break;
                        }
                    }
                }

            }
            else
            {
                int[] count = new int[10]; //初始化
                for (int i = 0; i < 10; i++)
                    count[i] = 0;

                foreach (Cellsd c in list)
                {
                    if (c.V < 0)
                    {
                        foreach (int v in c.CanList)
                            count[v]++;
                    }
                }
                for (int i = 0; i < count.Length; i++)
                {
                    if (count[i] == 1)
                    {
                        re = true;
                        if (testpos)
                            return re;
                        foreach (Cellsd c in list)
                            if (c.V < 0 && c.CanList.Contains(i))
                            {
                                c.CanList.Clear();
                                c.CanList.Add(i);
                                c.SetValueByCanList();
                            }
                    }
                }
            }
            return re;
        }
        
        private Cellsd[,] _Data;
        private bool _UseMode;
        private int _ActiveGongIndex;
        private List<Cellsd> _ActiveList;
        private List<Cellsd> _ActiveClickList;
        private List<CellG> _ListG;
        private List<CellC> _ListC;
        private List<CellR> _ListR;
        private List<CellList> _ListGCR;
        private int _TryCnt;
        private int _MaxBackDeepth;
    }

    public class CellIndex
    {
        public CellIndex(Cellsd cc,int index){
            Cell = cc;
            Index = index;
        }
        public Cellsd Cell {get;set;}
        public int Index {get;set;}
        public override string ToString()
        {
            return Index + "," + Cell;
        }

    }
}
